home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / useradd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-28  |  25.1 KB  |  1,256 lines

  1. /*
  2.  * Copyright 1991, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  */
  11.  
  12. #ifndef lint
  13. static    char    sccsid[] = "@(#)useradd.c    3.8    19:40:07    12/28/91";
  14. #endif
  15.  
  16. #include "config.h"
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <stdio.h>
  20. #include <errno.h>
  21. #include "pwd.h"
  22. #include <grp.h>
  23. #include <ctype.h>
  24. #include <fcntl.h>
  25. #include <time.h>
  26.  
  27. #ifdef    BSD
  28. #include <strings.h>
  29. #else
  30. #include <string.h>
  31. #endif
  32.  
  33. #include "shadow.h"
  34.  
  35. #ifdef    USE_SYSLOG
  36. #include <syslog.h>
  37.  
  38. #ifndef    LOG_WARN
  39. #define    LOG_WARN LOG_WARNING
  40. #endif
  41. #endif
  42.  
  43. gid_t    def_group;
  44. char    def_home[BUFSIZ];
  45. char    def_shell[BUFSIZ];
  46. char    def_template[BUFSIZ] = "/etc/skel";
  47. long    def_inactive;
  48. long    def_expire;
  49. char    def_file[] = "/etc/default/useradd";
  50.  
  51. #ifndef    NGROUPS_MAX
  52. #define    NGROUPS_MAX    64
  53. #endif
  54.  
  55. #define    VALID(s)    (strcspn (s, ":\n") == strlen (s))
  56.  
  57. char    user_name[BUFSIZ];
  58. uid_t    user_id;
  59. gid_t    user_gid;
  60. char    user_comment[BUFSIZ];
  61. char    user_home[BUFSIZ];
  62. char    user_shell[BUFSIZ];
  63. long    user_expire;
  64. int    user_ngroups;
  65. gid_t    user_groups[NGROUPS_MAX];
  66.  
  67. char    *Prog;
  68.  
  69. int    uflg;    /* specify user ID for new account                            */
  70. int    oflg;    /* permit non-unique user ID to be specified with -u          */
  71. int    gflg;    /* primary group ID  for new account                          */
  72. int    Gflg;    /* secondary group set for new account                        */
  73. int    dflg;    /* home directory for new account                             */
  74. int    bflg;    /* new default root of home directory                         */
  75. int    sflg;    /* shell program for new account                              */
  76. int    cflg;    /* comment (GECOS) field for new account                      */
  77. int    mflg;    /* create user's home directory if it doesn't exist           */
  78. int    kflg;    /* specify a directory to fill new user directory             */
  79. int    fflg;    /* days until account with expired password is locked         */
  80. int    eflg;    /* days after password changed before it becomes expired      */
  81. int    Dflg;    /* set/show new user default values                           */
  82.  
  83. #ifdef NDBM
  84. extern    int    pw_dbm_mode;
  85. extern    int    sp_dbm_mode;
  86. extern    int    gr_dbm_mode;
  87. #ifdef    SHADOWGRP
  88. extern    int    sg_dbm_mode;
  89. #endif
  90. #endif
  91. extern    FILE    *fopen();
  92. extern    int    fclose();
  93. extern    char    *malloc();
  94. extern    char    *mktemp();
  95.  
  96. extern    struct    group    *getgrnam();
  97. extern    struct    group    *getgrgid();
  98. extern    struct    group    *gr_next();
  99. extern    struct    group    *gr_locate();
  100. extern    int    gr_lock();
  101. extern    int    gr_unlock();
  102. extern    int    gr_rewind();
  103. extern    int    gr_open();
  104.  
  105. #ifdef    SHADOWGRP
  106. extern    struct    sgrp    *sgr_next();
  107. extern    int    sgr_lock();
  108. extern    int    sgr_unlock();
  109. extern    int    sgr_rewind();
  110. extern    int    sgr_open();
  111. #endif
  112.  
  113. extern    struct    passwd    *getpwnam();
  114. extern    struct    passwd    *pw_next();
  115. extern    int    pw_lock();
  116. extern    int    pw_unlock();
  117. extern    int    pw_rewind();
  118. extern    int    pw_open();
  119.  
  120. extern    int    spw_lock();
  121. extern    int    spw_unlock();
  122. extern    int    spw_open();
  123.  
  124. #define    DAY    (24L*3600L)
  125. #define    WEEK    (7*DAY)
  126.  
  127. #ifdef    ITI_AGING
  128. #define    SCALE    (1)
  129. #else
  130. #define    SCALE    (DAY)
  131. #endif
  132.  
  133. /*
  134.  * days and juldays are used to compute the number of days in the
  135.  * current month, and the cummulative number of days in the preceding
  136.  * months.  they are declared so that january is 1, not 0.
  137.  */
  138.  
  139. static    short    days[13] = { 0,
  140.     31,    28,    31,    30,    31,    30,    /* JAN - JUN */
  141.     31,    31,    30,    31,    30,    31 };    /* JUL - DEC */
  142.  
  143. static    short    juldays[13] = { 0,
  144.     0,    31,    59,    90,    120,    151,    /* JAN - JUN */
  145.     181,    212,    243,    273,    304,    334 };    /* JUL - DEC */
  146.  
  147. #ifdef    NEED_RENAME
  148. /*
  149.  * rename - rename a file to another name
  150.  *
  151.  *    rename is provided for systems which do not include the rename()
  152.  *    system call.
  153.  */
  154.  
  155. int
  156. rename (begin, end)
  157. char    *begin;
  158. char    *end;
  159. {
  160.     struct    stat    s1, s2;
  161.     extern    int    errno;
  162.     int    orig_err = errno;
  163.  
  164.     if (stat (begin, &s1))
  165.         return -1;
  166.  
  167.     if (stat (end, &s2)) {
  168.         errno = orig_err;
  169.     } else {
  170.  
  171.         /*
  172.          * See if this is a cross-device link.  We do this to
  173.          * insure that the link below has a chance of working.
  174.          */
  175.  
  176.         if (s1.st_dev != s2.st_dev) {
  177.             errno = EXDEV;
  178.             return -1;
  179.         }
  180.  
  181.         /*
  182.          * See if we can unlink the existing destination
  183.          * file.  If the unlink works the directory is writable,
  184.          * so there is no need here to figure that out.
  185.          */
  186.  
  187.         if (unlink (end))
  188.             return -1;
  189.     }
  190.  
  191.     /*
  192.      * Now just link the original name to the final name.  If there
  193.      * was no file previously, this link will fail if the target
  194.      * directory isn't writable.  The unlink will fail if the source
  195.      * directory isn't writable, but life stinks ...
  196.      */
  197.  
  198.     if (link (begin, end) || unlink (begin))
  199.         return -1;
  200.  
  201.     return 0;
  202. }
  203. #endif
  204.  
  205. /*
  206.  * strtoday - compute the number of days since 1970.
  207.  *
  208.  * the total number of days prior to the current date is
  209.  * computed.  january 1, 1970 is used as the origin with
  210.  * it having a day number of 0.
  211.  */
  212.  
  213. long
  214. strtoday (str)
  215. char    *str;
  216. {
  217.     char    slop[2];
  218.     int    month;
  219.     int    day;
  220.     int    year;
  221.     long    total;
  222.  
  223.     /*
  224.      * start by separating the month, day and year.  this is
  225.      * a chauvanistic program - it only takes date input in
  226.      * the standard USA format.
  227.      */
  228.  
  229.     if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
  230.         return -1;
  231.  
  232.     /*
  233.      * the month, day of the month, and year are checked for
  234.      * correctness and the year adjusted so it falls between
  235.      * 1970 and 2069.
  236.      */
  237.  
  238.     if (month < 1 || month > 12)
  239.         return -1;
  240.  
  241.     if (day < 1)
  242.         return -1;
  243.  
  244.     if ((month != 2 || (year % 4) != 0) && day > days[month])
  245.         return -1;
  246.     else if ((month == 2 && (year % 4) == 0) && day > 29)
  247.         return -1;
  248.  
  249.     if (year < 0)
  250.         return -1;
  251.     else if (year < 69)
  252.         year += 2000;
  253.     else if (year < 99)
  254.         year += 1900;
  255.  
  256.     if (year < 1970 || year > 2069)
  257.         return -1;
  258.  
  259.     /*
  260.      * the total number of days is the total number of days in all
  261.      * the whole years, plus the number of leap days, plus the
  262.      * number of days in the whole months preceding, plus the number
  263.      * of days so far in the month.
  264.      */
  265.  
  266.     total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
  267.     total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
  268.     total += (long) day - 1;
  269.  
  270.     return total;
  271. }
  272.  
  273. /*
  274.  * add_list - add a member to a list of group members
  275.  *
  276.  *    the array of member names is searched for the new member
  277.  *    name, and if not present it is added to a freshly allocated
  278.  *    list of users.
  279.  */
  280.  
  281. char **
  282. add_list (list, member)
  283. char    **list;
  284. char    *member;
  285. {
  286.     int    i;
  287.     char    **tmp;
  288.  
  289.     /*
  290.      * Scan the list for the new name.  Return the original list
  291.      * pointer if it is present.
  292.      */
  293.  
  294.     for (i = 0;list[i] != (char *) 0;i++)
  295.         if (strcmp (list[i], member) == 0)
  296.             return list;
  297.  
  298.     /*
  299.      * Allocate a new list pointer large enough to hold all the
  300.      * old entries, and the new entries as well.
  301.      */
  302.  
  303.     if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
  304.         return 0;
  305.  
  306.     /*
  307.      * Copy the original list to the new list, then append the
  308.      * new member and NULL terminate the result.  This new list
  309.      * is returned to the invoker.
  310.      */
  311.  
  312.     for (i = 0;list[i] != (char *) 0;i++)
  313.         tmp[i] = list[i];
  314.  
  315.     tmp[i++] = strdup (member);
  316.     tmp[i] = (char *) 0;
  317.  
  318.     return tmp;
  319. }
  320.  
  321. /*
  322.  * get_defaults - read the defaults file
  323.  *
  324.  *    get_defaults() reads the defaults file for this command.  It sets
  325.  *    the various values from the file, or uses built-in default values
  326.  *    if the file does not exist.
  327.  */
  328.  
  329. void
  330. get_defaults ()
  331. {
  332.     FILE    *fp;
  333.     char    buf[BUFSIZ];
  334.     char    *cp;
  335.     struct    group    *grp;
  336.  
  337.     /*
  338.      * Open the defaults file for reading.
  339.      */
  340.  
  341.     if (! (fp = fopen (def_file, "r"))) {
  342.  
  343.         /*
  344.          * No defaults file - set up the defaults that are given
  345.          * in the documentation.
  346.          */
  347.  
  348.         def_group = 1;
  349.         strcpy (def_home, "/home");
  350.         def_inactive = 0;
  351.         def_expire = 0;
  352.         return;
  353.     }
  354.  
  355.     /*
  356.      * Read the file a line at a time.  Only the lines that have
  357.      * relevant values are used, everything else can be ignored.
  358.      */
  359.  
  360.     while (fgets (buf, BUFSIZ, fp)) {
  361.         if (cp = strrchr (buf, '\n'))
  362.             *cp = '\0';
  363.  
  364.         /*
  365.          * Primary GROUP identifier
  366.          */
  367.  
  368.         if (strncmp ("GROUP=", buf, 6) == 0) {
  369.             cp = buf + 6;
  370.             if (isdigit (*cp))
  371.                 def_group = atoi (cp);
  372.             else if (grp = getgrnam (cp))
  373.                 def_group = grp->gr_gid;
  374.             else
  375.                 fprintf (stderr, "%s: unknown group %s\n",
  376.                     Prog, cp);
  377.         }
  378.         
  379.         /*
  380.          * Default HOME filesystem
  381.          */
  382.          
  383.         else if (strncmp ("HOME=", buf, 5) == 0) {
  384.             strncpy (def_home, buf + 5, BUFSIZ);
  385.         }
  386.  
  387.         /*
  388.          * Default Login Shell command
  389.          */
  390.  
  391.         else if (strncmp ("SHELL=", buf, 6) == 0) {
  392.             strncpy (def_shell, buf + 6, BUFSIZ);
  393.         }
  394.  
  395.         /*
  396.          * Default Password Inactive value
  397.          */
  398.  
  399.         else if (strncmp ("INACTIVE=", buf, 9) == 0) {
  400.             def_inactive = atoi (buf + 9);
  401.         }
  402.         
  403.         /*
  404.          * Default Password Expiration value
  405.          */
  406.  
  407.         else if (strncmp ("EXPIRE=", buf, 7) == 0) {
  408.             def_expire = atoi (buf + 7);
  409.         }
  410.     }
  411. }
  412.  
  413. /*
  414.  * show_defaults - show the contents of the defaults file
  415.  *
  416.  *    show_defaults() displays the values that are used from the default
  417.  *    file and the built-in values.
  418.  */
  419.  
  420. void
  421. show_defaults ()
  422. {
  423.     printf ("GROUP=%d\n", def_group);
  424.     printf ("HOME=%s\n", def_home);
  425.     printf ("INACTIVE=%d\n", def_inactive);
  426.     printf ("EXPIRE=%d\n", def_expire);
  427. }
  428.  
  429. /*
  430.  * set_defaults - write new defaults file
  431.  *
  432.  *    set_defaults() re-writes the defaults file using the values that
  433.  *    are currently set.  Duplicated lines are pruned, missing lines are
  434.  *    added, and unrecognized lines are copied as is.
  435.  */
  436.  
  437. int
  438. set_defaults ()
  439. {
  440.     FILE    *ifp;
  441.     FILE    *ofp;
  442.     char    buf[BUFSIZ];
  443.     static    char    new_file[] = "/etc/default/nuaddXXXXXX";
  444.     char    *cp;
  445.     int    out_group = 0;
  446.     int    out_home = 0;
  447.     int    out_inactive = 0;
  448.     int    out_expire = 0;
  449.  
  450.     /*
  451.      * Create a temporary file to copy the new output to.
  452.      */
  453.  
  454.     mktemp (new_file);
  455.     if (! (ofp = fopen (new_file, "w"))) {
  456.         fprintf (stderr, "%s: cannot create new defaults file\n", Prog);
  457.         return -1;
  458.     }
  459.  
  460.     /*
  461.      * Open the existing defaults file and copy the lines to the
  462.      * temporary files, using any new values.  Each line is checked
  463.      * to insure that it is not output more than once.
  464.      */
  465.  
  466.     if (ifp = fopen (def_file, "r")) {
  467.         while (fgets (buf, BUFSIZ, ifp)) {
  468.             if (cp = strrchr (buf, '\n'))
  469.                 *cp = '\0';
  470.  
  471.             if (strncmp ("GROUP=", buf, 6) == 0) {
  472.                 if (! out_group)
  473.                     fprintf (ofp, "GROUP=%d\n", def_group);
  474.  
  475.                 out_group++;
  476.             } else if (strncmp ("HOME=", buf, 5) == 0) {
  477.                 if (! out_home)
  478.                     fprintf (ofp, "HOME=%s\n", def_home);
  479.  
  480.                 out_home++;
  481.             } else if (strncmp ("INACTIVE=", buf, 9) == 0) {
  482.                 if (! out_inactive)
  483.                     fprintf (ofp, "INACTIVE=%d\n",
  484.                         def_inactive);
  485.  
  486.                 out_inactive++;
  487.             } else if (strncmp ("EXPIRE=", buf, 7) == 0) {
  488.                 if (! out_expire)
  489.                     fprintf (ofp, "EXPIRE=%d\n",
  490.                         def_expire);
  491.  
  492.                 out_expire++;
  493.             } else
  494.                 fprintf (ofp, "%s\n", buf);
  495.         }
  496.         fclose ((FILE *) ifp);
  497.     }
  498.  
  499.     /*
  500.      * Check each line to insure that every line was output.  This
  501.      * causes new values to be added to a file which did not previously
  502.      * have an entry for that value.
  503.      */
  504.  
  505.     if (! out_group)
  506.         fprintf (ofp, "GROUP=%d\n", def_group);
  507.  
  508.     if (! out_home)
  509.         fprintf (ofp, "HOME=%s\n", def_home);
  510.  
  511.     if (! out_inactive)
  512.         fprintf (ofp, "INACTIVE=%d\n", def_inactive);
  513.  
  514.     if (! out_expire)
  515.         fprintf (ofp, "EXPIRE=%d\n", def_expire);
  516.  
  517.     /*
  518.      * Flush and close the file.  Check for errors to make certain
  519.      * the new file is intact.
  520.      */
  521.  
  522.     (void) fflush (ofp);
  523.     if (ferror (ofp) || fclose ((FILE *) ofp)) {
  524.         unlink (new_file);
  525.         return -1;
  526.     }
  527.  
  528.     /*
  529.      * Rename the current default file to its backup name.
  530.      */
  531.  
  532.     sprintf (buf, "%s-", def_file);
  533.     if (rename (def_file, buf) && errno != ENOENT) {
  534.         sprintf (buf, "%s: rename: %s", Prog, def_file);
  535.         perror (buf);
  536.         unlink (new_file);
  537.         return -1;
  538.     }
  539.  
  540.     /*
  541.      * Rename the new default file to its correct name.
  542.      */
  543.  
  544.     if (rename (new_file, def_file)) {
  545.         sprintf (buf, "%s: rename: %s", Prog, new_file);
  546.         perror (buf);
  547.         return -1;
  548.     }
  549. #ifdef    USE_SYSLOG
  550.     syslog (LOG_INFO,
  551.         "defaults: group=%d, home=%s, inactive=%d, expire=%d\n",
  552.         def_group, def_home, def_inactive, def_expire);
  553. #endif
  554.     return 0;
  555. }
  556.  
  557. /*
  558.  * get_groups - convert a list of group names to an array of group IDs
  559.  *
  560.  *    get_groups() takes a comma-separated list of group names and
  561.  *    converts it to an array of group ID values.  Any unknown group
  562.  *    names are reported as errors.
  563.  */
  564.  
  565. int
  566. get_groups (list)
  567. char    *list;
  568. {
  569.     char    *cp;
  570.     struct    group    *grp;
  571.     int    errors = 0;
  572.  
  573.     /*
  574.      * Initialize the list to be empty
  575.      */
  576.  
  577.     user_ngroups = 0;
  578.  
  579.     if (! *list)
  580.         return 0;
  581.  
  582.     /*
  583.      * So long as there is some data to be converted, strip off
  584.      * each name and look it up.  A mix of numerical and string
  585.      * values for group identifiers is permitted.
  586.      */
  587.  
  588.     do {
  589.         /*
  590.          * Strip off a single name from the list
  591.          */
  592.  
  593.         if (cp = strchr (list, ','))
  594.             *cp++ = '\0';
  595.  
  596.         /*
  597.          * Names starting with digits are treated as numerical
  598.          * GID values, otherwise the string is looked up as is.
  599.          */
  600.  
  601.         if (isdigit (*list))
  602.             grp = getgrgid (atoi (list));
  603.         else
  604.             grp = getgrnam (list);
  605.  
  606.         /*
  607.          * There must be a match, either by GID value or by
  608.          * string name.
  609.          */
  610.  
  611.         if (! grp) {
  612.             fprintf (stderr, "%s: unknown group %s\n", Prog, list);
  613.             errors++;
  614.         }
  615.  
  616.         /*
  617.          * Add the GID value from the group file to the user's
  618.          * list of groups.
  619.          */
  620.  
  621.         user_groups[user_ngroups++] = grp->gr_gid;
  622.  
  623.         list = cp;
  624.     } while (list);
  625.  
  626.     /*
  627.      * Any errors in finding group names are fatal
  628.      */
  629.  
  630.     if (errors)
  631.         return -1;
  632.  
  633.     return 0;
  634. }
  635.  
  636. /*
  637.  * usage - display usage message and exit
  638.  */
  639.  
  640. usage ()
  641. {
  642.     fprintf (stderr,
  643.         "usage:\t%s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
  644.     fprintf (stderr,
  645.         "\t\t[-d home] [-s shell] [-c comment] [-m [-k template]]\n");
  646.     fprintf (stderr,
  647.         "\t\t[-f inactive] [-e expire] name\n");
  648.  
  649.     fprintf (stderr,
  650.         "\t%s -D [-g group] [-b base] [-f inactive] [-e expire]\n",
  651.             Prog);
  652.  
  653.     exit (1);
  654. }
  655.  
  656. /*
  657.  * new_pwent - initialize the values in a password file entry
  658.  *
  659.  *    new_pwent() takes all of the values that have been entered and
  660.  *    fills in a (struct passwd) with them.
  661.  */
  662.  
  663. void
  664. new_pwent (pwent)
  665. struct    passwd    *pwent;
  666. {
  667.     memset (pwent, 0, sizeof *pwent);
  668.     pwent->pw_name = user_name;
  669.     pwent->pw_passwd = "*";
  670.     pwent->pw_age = "";
  671.     pwent->pw_uid = user_id;
  672.     pwent->pw_gid = user_gid;
  673.     pwent->pw_gecos = user_comment;
  674.     pwent->pw_comment = "";
  675.     pwent->pw_dir = user_home;
  676.     pwent->pw_shell = user_shell;
  677. }
  678.  
  679. /*
  680.  * new_spent - initialize the values in a shadow password file entry
  681.  *
  682.  *    new_spent() takes all of the values that have been entered and
  683.  *    fills in a (struct spwd) with them.
  684.  */
  685.  
  686. void
  687. new_spent (spent)
  688. struct    spwd    *spent;
  689. {
  690.     memset (spent, 0, sizeof *spent);
  691.     spent->sp_namp = user_name;
  692.     spent->sp_pwdp = "!";
  693.     spent->sp_lstchg = 0;
  694.     spent->sp_min = 0;
  695.     spent->sp_max = def_expire;
  696.     spent->sp_warn = 0;
  697.     spent->sp_inact = def_inactive;
  698.     spent->sp_expire = user_expire;
  699. }
  700.  
  701. /*
  702.  * grp_update - add user to secondary group set
  703.  *
  704.  *    grp_update() takes the secondary group set given in user_groups
  705.  *    and adds the user to each group given by that set.
  706.  */
  707.  
  708. void
  709. grp_update ()
  710. {
  711.     int    i;
  712.     struct    group    *grp;
  713. #ifdef    SHADOWGRP
  714.     struct    sgrp    *sgrp;
  715. #endif
  716.  
  717.     /*
  718.      * Lock and open the group file.  This will load all of the group
  719.      * entries.
  720.      */
  721.  
  722.     if (! gr_lock ()) {
  723.         fprintf (stderr, "%s: error locking group file\n", Prog);
  724.         exit (1);
  725.     }
  726.     if (! gr_open (O_RDWR)) {
  727.         fprintf (stderr, "%s: error opening group file\n", Prog);
  728.         exit (1);
  729.     }
  730. #ifdef    SHADOWGRP
  731.     if (! sgr_lock ()) {
  732.         fprintf (stderr, "%s: error locking shadow group file\n", Prog);
  733.         exit (1);
  734.     }
  735.     if (! sgr_open (O_RDWR)) {
  736.         fprintf (stderr, "%s: error opening shadow group file\n", Prog);
  737.         exit (1);
  738.     }
  739. #endif
  740.  
  741.     /*
  742.      * Scan through the entire group file looking for the groups that
  743.      * the user is a member of.
  744.      */
  745.  
  746.     for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  747.  
  748.         /*
  749.          * See if the user specified this group as one of their
  750.          * concurrent groups.
  751.          */
  752.  
  753.         for (i = 0;i < user_ngroups;i++)
  754.             if (grp->gr_gid == user_groups[i])
  755.                 break;
  756.  
  757.         if (i == user_ngroups)
  758.             continue;
  759.  
  760.         /* 
  761.          * Add the username to the list of group members and
  762.          * update the group entry to reflect the change.
  763.          */
  764.  
  765.         grp->gr_mem = add_list (grp->gr_mem, user_name);
  766.         if (! gr_update (grp)) {
  767.             fprintf (stderr, "%s: error adding new group entry\n",
  768.                 Prog);
  769.             exit (1);
  770.         }
  771. #ifdef    NDBM
  772.         /*
  773.          * Update the DBM group file with the new entry as well.
  774.          */
  775.  
  776.         if (! gr_dbm_update (grp)) {
  777.             fprintf (stderr, "%s: cannot add new dbm group entry\n",
  778.                 Prog);
  779.             exit (1);
  780.         }
  781.         endgrent ();
  782. #endif
  783. #ifdef    USE_SYSLOG
  784.         syslog (LOG_INFO, "add `%s' to group `%s'\n",
  785.             user_name, grp->gr_name);
  786. #endif
  787.     }
  788.  
  789. #ifdef    SHADOWGRP
  790.     /*
  791.      * Scan through the entire shadow group file looking for the groups
  792.      * that the user is a member of.  The administrative list isn't
  793.      * modified.
  794.      */
  795.  
  796.     for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
  797.  
  798.         /*
  799.          * See if the user specified this group as one of their
  800.          * concurrent groups.
  801.          */
  802.  
  803.         for (i = 0;i < user_ngroups;i++) {
  804.             if (! (grp = gr_locate (sgrp->sg_name)))
  805.                 continue;
  806.  
  807.             if (grp->gr_gid == user_groups[i])
  808.                 break;
  809.         }
  810.         if (i == user_ngroups)
  811.             continue;
  812.  
  813.         /* 
  814.          * Add the username to the list of group members and
  815.          * update the group entry to reflect the change.
  816.          */
  817.  
  818.         sgrp->sg_mem = add_list (sgrp->sg_mem, user_name);
  819.         if (! sgr_update (sgrp)) {
  820.             fprintf (stderr, "%s: error adding new group entry\n",
  821.                 Prog);
  822.             exit (1);
  823.         }
  824. #ifdef    NDBM
  825.         /*
  826.          * Update the DBM group file with the new entry as well.
  827.          */
  828.  
  829.         if (! sgr_dbm_update (sgrp)) {
  830.             fprintf (stderr, "%s: cannot add new dbm group entry\n",
  831.                 Prog);
  832.             exit (1);
  833.         }
  834.         endsgent ();
  835. #endif
  836. #ifdef    USE_SYSLOG
  837.         syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
  838.             user_name, sgrp->sg_name);
  839. #endif
  840.     }
  841. #endif
  842. }
  843.  
  844. /*
  845.  * find_new_uid - find the next available UID
  846.  *
  847.  *    find_new_uid() locates the next highest unused UID in the password
  848.  *    file, or checks the given user ID against the existing ones for
  849.  *    uniqueness.
  850.  */
  851.  
  852. int
  853. find_new_uid ()
  854. {
  855.     struct    passwd    *pwd;
  856.  
  857.     /*
  858.      * Start with some UID value if the user didn't provide us with
  859.      * one already.
  860.      */
  861.  
  862.     if (! uflg)
  863.         user_id = 100;
  864.  
  865.     /*
  866.      * Search the entire password file, either looking for this
  867.      * UID (if the user specified one with -u) or looking for the
  868.      * largest unused value.
  869.      */
  870.  
  871.     for (pw_rewind (), pwd = pw_next ();pwd;pwd = pw_next ()) {
  872.         if (strcmp (user_name, pwd->pw_name) == 0) {
  873.             fprintf (stderr, "%s: name %s is not unique\n",
  874.                 Prog, user_name);
  875.             exit (1);
  876.         }
  877.         if (uflg && user_id == pwd->pw_uid) {
  878.             fprintf (stderr, "%s: uid %d is not unique\n",
  879.                 Prog, user_id);
  880.             exit (1);
  881.         }
  882.         if (! uflg && pwd->pw_uid >= user_id)
  883.             user_id = pwd->pw_uid + 1;
  884.     }
  885. }
  886.  
  887. /*
  888.  * process_flags - perform command line argument setting
  889.  *
  890.  *    process_flags() interprets the command line arguments and sets
  891.  *    the values that the user will be created with accordingly.  The
  892.  *    values are checked for sanity.
  893.  */
  894.  
  895. void
  896. process_flags (argc, argv)
  897. int    argc;
  898. char    **argv;
  899. {
  900.     extern    int    optind;
  901.     extern    char    *optarg;
  902.     struct    group    *grp;
  903.     int    anyflag = 0;
  904.     int    arg;
  905.  
  906.     while ((arg = getopt (argc, argv, "Du:og:G:d:s:c:mk:f:e:b:")) != EOF) {
  907.         switch (arg) {
  908.             case 'b':
  909.                 if (! VALID (optarg)) {
  910.                     fprintf (stderr,
  911.                         "%s: invalid field `%s'\n",
  912.                         Prog, optarg);
  913.                     exit (3);
  914.                 }
  915.                 bflg++;
  916.                 if (! Dflg)
  917.                     usage ();
  918.  
  919.                 strncpy (def_home, optarg, BUFSIZ);
  920.                 break;
  921.             case 'c':
  922.                 if (! VALID (optarg)) {
  923.                     fprintf (stderr,
  924.                         "%s: invalid field `%s'\n",
  925.                         Prog, optarg);
  926.                     exit (3);
  927.                 }
  928.                 cflg++;
  929.                 strncpy (user_comment, optarg, BUFSIZ);
  930.                 break;
  931.             case 'd':
  932.                 if (! VALID (optarg)) {
  933.                     fprintf (stderr,
  934.                         "%s: invalid field `%s'\n",
  935.                         Prog, optarg);
  936.                     exit (3);
  937.                 }
  938.                 dflg++;
  939.                 strncpy (user_home, optarg, BUFSIZ);
  940.                 break;
  941.             case 'D':
  942.                 if (anyflag)
  943.                     usage ();
  944.  
  945.                 Dflg++;
  946.                 break;
  947.             case 'e':
  948.                 eflg++;
  949.                 if (Dflg)
  950.                     def_expire = atoi (optarg);
  951.                 else {
  952.                     user_expire = strtoday (optarg);
  953. #ifdef    ITI_AGING
  954.                     user_expire *= DAY;
  955. #endif
  956.                 }
  957.                 break;
  958.             case 'f':
  959.                 fflg++;
  960.                 def_inactive = atoi (optarg);
  961.                 break;
  962.             case 'g':
  963.                 gflg++;
  964.                 if (isdigit (optarg[0]))
  965.                     grp = getgrgid (atoi (optarg));
  966.                 else
  967.                     grp = getgrnam (optarg);
  968.  
  969.                 if (! grp) {
  970.                     fprintf (stderr,
  971.                         "%s: unknown group %s\n",
  972.                         Prog, optarg);
  973.                     exit (1);
  974.                 }
  975.                 if (Dflg)
  976.                     def_group = grp->gr_gid;
  977.                 else
  978.                     user_gid = grp->gr_gid;
  979.                 break;
  980.             case 'G':
  981.                 Gflg++;
  982.                 if (get_groups (optarg))
  983.                     exit (1);
  984.  
  985.                 break;
  986.             case 'k':
  987.                 if (! mflg)
  988.                     usage ();
  989.  
  990.                 strncpy (def_template, optarg, BUFSIZ);
  991.                 kflg++;
  992.                 break;
  993.             case 'm':
  994.                 mflg++;
  995.                 break;
  996.             case 'o':
  997.                 if (! uflg)
  998.                     usage ();
  999.  
  1000.                 oflg++;
  1001.                 break;
  1002.             case 's':
  1003.                 if (! VALID (optarg)) {
  1004.                     fprintf (stderr,
  1005.                         "%s: invalid field `%s'\n",
  1006.                         Prog, optarg);
  1007.                     exit (3);
  1008.                 }
  1009.                 sflg++;
  1010.                 strncpy (user_shell, optarg, BUFSIZ);
  1011.                 break;
  1012.             case 'u':
  1013.                 uflg++;
  1014.                 user_id = atoi (optarg);
  1015.                 break;
  1016.             default:
  1017.                 usage ();
  1018.         }
  1019.         anyflag++;
  1020.     }
  1021.     if (! Dflg && optind == argc - 1)
  1022.         strcpy (user_name, argv[argc - 1]);
  1023.  
  1024.     if (! dflg)
  1025.         sprintf (user_home, "%s/%s", def_home, user_name);
  1026.  
  1027.     if (! gflg)
  1028.         user_gid = def_group;
  1029.  
  1030.     if (Dflg) {
  1031.         if (optind != argc)
  1032.             usage ();
  1033.  
  1034.         if (uflg || oflg || Gflg || dflg ||
  1035.                 sflg || cflg || mflg || kflg)
  1036.             usage ();
  1037.     } else {
  1038.         if (optind != argc - 1)
  1039.             usage ();
  1040.     }
  1041. }
  1042.  
  1043. /*
  1044.  * close_files - close all of the files that were opened
  1045.  *
  1046.  *    close_files() closes all of the files that were opened for this
  1047.  *    new user.  This causes any modified entries to be written out.
  1048.  */
  1049.  
  1050. close_files ()
  1051. {
  1052.     if (! pw_close ()) {
  1053.         fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
  1054.         exit (1);
  1055.     }
  1056.     if (! spw_close ()) {
  1057.         fprintf (stderr, "%s: cannot rewrite shadow password file\n",    
  1058.             Prog);
  1059.         exit (1);
  1060.     }
  1061.     if (user_ngroups > 0) {
  1062.         if (! gr_close ()) {
  1063.             fprintf (stderr, "%s: cannot rewrite group file\n",
  1064.                 Prog);
  1065.             exit (1);
  1066.         }
  1067.         (void) gr_unlock ();
  1068. #ifdef    SHADOWGRP
  1069.         if (! sgr_close ()) {
  1070.             fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  1071.                 Prog);
  1072.             exit (1);
  1073.         }
  1074.         (void) sgr_unlock ();
  1075. #endif
  1076.     }
  1077.     (void) spw_unlock ();
  1078.     (void) pw_unlock ();
  1079. }
  1080.  
  1081. /*
  1082.  * open_files - lock and open the password files
  1083.  *
  1084.  *    open_files() opens the two password files.
  1085.  */
  1086.  
  1087. open_files ()
  1088. {
  1089.     if (! pw_lock ()) {
  1090.         fprintf (stderr, "%s: unable to lock password file\n", Prog);
  1091.         exit (1);
  1092.     }
  1093.     if (! pw_open (O_RDWR)) {
  1094.         fprintf (stderr, "%s: unable to open password file\n", Prog);
  1095.         exit (1);
  1096.     }
  1097.     if (! spw_lock ()) {
  1098.         fprintf (stderr, "%s: cannot lock shadow password file\n", Prog);
  1099.         exit (1);
  1100.     }
  1101.     if (! spw_open (O_RDWR)) {
  1102.         fprintf (stderr, "%s: cannot open shadow password file\n", Prog);
  1103.         exit (1);
  1104.     }
  1105. }
  1106.  
  1107. /*
  1108.  * usr_update - create the user entries
  1109.  *
  1110.  *    usr_update() creates the password file entries for this user
  1111.  *    and will update the group entries if required.
  1112.  */
  1113.  
  1114. usr_update ()
  1115. {
  1116.     struct    passwd    pwent;
  1117.     struct    spwd    spent;
  1118.  
  1119.     if (! oflg)
  1120.         find_new_uid ();
  1121.  
  1122.     new_pwent (&pwent);
  1123.     if (! pw_update (&pwent)) {
  1124.         fprintf (stderr, "%s: error adding new password entry\n", Prog);
  1125.         exit (1);
  1126.     }
  1127.     new_spent (&spent);
  1128.     if (! spw_update (&spent)) {
  1129.         fprintf (stderr, "%s: error adding new shadow password entry\n",
  1130.             Prog);
  1131.         exit (1);
  1132.     }
  1133. #if defined(DBM) || defined(NDBM)
  1134.     if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (&pwent)) {
  1135.         fprintf (stderr, "%s: error updating password dbm entry\n",
  1136.             Prog);
  1137.         exit (1);
  1138.     }
  1139.     endpwent ();
  1140. #endif
  1141. #ifdef    NDBM
  1142.     if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (&spent)) {
  1143.         fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
  1144.             Prog);
  1145.         exit (1);
  1146.     }
  1147.     endspent ();
  1148. #endif
  1149. #ifdef    USE_SYSLOG
  1150.     syslog (LOG_INFO,
  1151.         "new user: name=%s, uid=%d, gid=%d, home=%s, shell=%s\n",
  1152.         user_name, user_id, user_gid, user_home, user_shell);
  1153. #endif
  1154.     if (user_ngroups > 0)
  1155.         grp_update ();
  1156. }
  1157.  
  1158. /*
  1159.  * create_home - create the user's home directory
  1160.  *
  1161.  *    create_home() creates the user's home directory if it does not
  1162.  *    already exist.  It will be created mode 755 owned by the user
  1163.  *    with the user's default group.
  1164.  */
  1165.  
  1166. create_home ()
  1167. {
  1168.     if (access (user_home, 0)) {
  1169.         if (mkdir (user_home, 0755)) {
  1170.             fprintf (stderr, "%s: cannot create directory %s\n",
  1171.                 Prog, user_home);
  1172.             exit (1);
  1173.         }
  1174.         chown (user_home, user_id, user_gid);
  1175.         chmod (user_home, 0755);
  1176.     }
  1177. }
  1178.  
  1179. /*
  1180.  * main - useradd command
  1181.  */
  1182.  
  1183. main (argc, argv)
  1184. int    argc;
  1185. char    **argv;
  1186. {
  1187.     /*
  1188.      * Get my name so that I can use it to report errors.
  1189.      */
  1190.  
  1191.     if (Prog = strrchr (argv[0], '/'))
  1192.         Prog++;
  1193.     else
  1194.         Prog = argv[0];
  1195.  
  1196. #ifdef    USE_SYSLOG
  1197.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  1198. #endif
  1199.  
  1200.     /*
  1201.      * The open routines for the NDBM files don't use read-write
  1202.      * as the mode, so we have to clue them in.
  1203.      */
  1204.  
  1205. #ifdef    NDBM
  1206.     pw_dbm_mode = O_RDWR;
  1207.     sp_dbm_mode = O_RDWR;
  1208.     gr_dbm_mode = O_RDWR;
  1209. #ifdef    SHADOWGRP
  1210.     sg_dbm_mode = O_RDWR;
  1211. #endif
  1212. #endif
  1213.     get_defaults ();
  1214.  
  1215.     process_flags (argc, argv);
  1216.  
  1217.     /*
  1218.      * See if we are messing with the defaults file, or creating
  1219.      * a new user.
  1220.      */
  1221.  
  1222.     if (Dflg) {
  1223.         if (gflg || bflg || fflg || eflg)
  1224.             exit (set_defaults () ? 1:0);
  1225.  
  1226.         show_defaults ();
  1227.         exit (0);
  1228.     }
  1229.  
  1230.     /*
  1231.      * Start with a quick check to see if the user exists.
  1232.      */
  1233.  
  1234.     if (getpwnam (user_name)) {
  1235.         fprintf (stderr, "%s: user %s exists\n", Prog, user_name);
  1236.         exit (1);
  1237.     }
  1238.  
  1239.     /*
  1240.      * Do the hard stuff - open the files, create the user entries,
  1241.      * create the home directory, then close and update the files.
  1242.      */
  1243.  
  1244.     open_files ();
  1245.  
  1246.     usr_update ();
  1247.  
  1248.     if (mflg) {
  1249.         create_home ();
  1250.         copy_tree (def_template, user_home, user_id, user_gid);
  1251.     }
  1252.     close_files ();
  1253.     exit (0);
  1254.     /*NOTREACHED*/
  1255. }
  1256.